export interface ClassProps {
    retry?: number;
    headers?: {[key: string]: string};
    bodyPip?: Function;
    beforeResponse?: Function;
    beforeRequest?: Function;
    afterResponse?: Function;
    errorResponse?: Function;
}
export interface ApiModule {
    method: 'POST' | 'GET' | 'PUT' | 'DELETE';
    url: string;
    headers?: {
        [key: string]: string;
    };
    body?: {
        [key: string]: any;
    };
    isFormData?: boolean;
}
export interface Module {
    name: string;
    [key: string]: ApiModule | string;
}
interface RequestParams {
    query?: {[key: string]: any};
    path?: string[];
    body?: {[key: string]: any};
    headers?: {[key: string]: any};
}

class jRequest {
    constructor(props: ClassProps) {
        const {
            beforeResponse = (res: any) => res.json(),
            headers = {},
            retry = 2,
            bodyPip = (data: any) => {
                return JSON.stringify({
                    data
                });
            },
            afterResponse = (resolve: Function) => {
                resolve();
            },
            errorResponse = (reject: Function) => {
                reject();
            },
            beforeRequest = undefined
        } = props;
        this._retry = retry;
        this._bodyPip = bodyPip;
        this._headers = {'Content-Type': 'application/json; charset=UTF-8', ...headers};
        this._beforeResponse = beforeResponse;
        this._beforeRequest = beforeRequest;
        this._afterResponse = afterResponse;
        this._errorResponse = errorResponse;
    }
    _retry: number;
    _times: number = 1;
    _bodyPip: Function | undefined;
    _headers = {};
    _beforeResponse: Function | undefined;
    _afterResponse: Function;
    _errorResponse: Function;
    _beforeRequest: Function;
    _request = (requestOption: ApiModule = {url: '', method: 'GET'}, moduleName: string, apiName: string) => {
        const options: {[key: string]: any} = {
            headers: {},
            method: 'get'
        };
        const {url, headers, method, body, isFormData} = requestOption;
        options.method = method;
        options.headers = Object.assign(this._headers, headers);

        if (method.toLowerCase() !== 'get') {
            if (isFormData) {
                options.body = body;
                delete options.headers['Content-Type'];
            } else {
                if (this._bodyPip) {
                    options.body = this._bodyPip(body);
                } else {
                    options.body = JSON.stringify({data: body});
                }
            }
        }
        let optResult = {
            url: url,
            options: options
        };
        if (this._beforeRequest) {
            optResult = this._beforeRequest(url, options);
        }

        return new Promise((resolve, reject) => {
            fetch(optResult.url, optResult.options)
                .then((res) => {
                    return this._beforeResponse(res);
                })
                .then((res: any) => {
                    this._times++;
                    this._afterResponse(
                        () => {
                            this._times = 1;
                            resolve(res);
                        },
                        res,
                        () => {
                            if (this._times > this._retry) {
                                return;
                            } else {
                                this._request(requestOption, moduleName, apiName);
                            }
                        }
                    );
                })
                .catch((err) => {
                    this._errorResponse(() => {
                        this._times = 1;
                        reject(err);
                    });
                });
        });
    };
    registerModule = (module: Module) => {
        //@ts-ignore
        const moduleF: {
            //@ts-ignore
            name: string;
            [key: string]: Function;
        } = {
            name: module.name
        };
        Object.keys(module).map((mKey) => {
            moduleF[mKey] = (params: RequestParams = {}) => {
                const {query, path, body, headers} = params;
                if (mKey === 'name') {
                    return module.name;
                }
                //@ts-ignore
                let {url} = module[mKey];
                //@ts-ignore
                const {method, isFormData} = module[mKey];
                //处理path参数
                if (Array.isArray(path) && path.length > 0) {
                    url += path.join('/');
                }
                //处理query参数
                if (query && Object.keys(query).length > 0) {
                    const arr = [];
                    let queryStr = '';
                    for (const k in query) {
                        if (k) {
                            if (query[k]) {
                                arr.push(`${k}=${encodeURIComponent(query[k])}`);
                            } else {
                                arr.push(`${k}=${query[k]}`);
                            }
                        }
                    }
                    if (arr.length > 0) {
                        queryStr = '?' + arr.join('&');
                    }
                    url = url + queryStr;
                }
                return this._request(
                    {
                        url,
                        method,
                        headers,
                        body,
                        isFormData
                    },
                    module.name,
                    mKey
                );
            };
        });
        return moduleF;
    };
}

export default jRequest;
